package com.xunlai.infra.permission.manager.authority.sercurity.user;

import com.xunlai.infra.permission.common.security.TenantUser;
import com.xunlai.infra.permission.storage.entity.AuthGroupCrew;
import com.xunlai.infra.permission.storage.entity.Employee;
import com.xunlai.infra.permission.storage.entity.RoleRelation;
import com.xunlai.infra.permission.storage.entity.UserAccount;
import com.xunlai.infra.permission.storage.repository.AuthGroupCrewRepository;
import com.xunlai.infra.permission.storage.repository.RoleRelationRepository;
import com.xunlai.infra.permission.storage.repository.UserAccountRepository;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author liang
 * @date 12/6/22 10:16 AM
 */
public class DelegateUserDetailsManager implements UserDetailsManager {

    private final UserAccountRepository userAccountRepository;

    private final RoleRelationRepository roleRelationRepository;

    private final AuthGroupCrewRepository authGroupCrewRepository;


    public DelegateUserDetailsManager(UserAccountRepository userAccountRepository, RoleRelationRepository roleRelationRepository, AuthGroupCrewRepository authGroupCrewRepository) {
        this.userAccountRepository = userAccountRepository;
        this.roleRelationRepository = roleRelationRepository;
        this.authGroupCrewRepository = authGroupCrewRepository;
    }

    @Override
    public void createUser(UserDetails user) {

    }

    @Override
    public void updateUser(UserDetails user) {

    }

    @Override
    public void deleteUser(String username) {

    }

    @Override
    public void changePassword(String oldPassword, String newPassword) {

    }

    @Override
    public boolean userExists(String username) {
        return userAccountRepository.existsByAccount(username);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<UserAccount> userAccountOptional = userAccountRepository.findByAccount(username);
        if(userAccountOptional.isEmpty()){
            throw new UsernameNotFoundException("账号不存在");
        }
        UserAccount userAccount = userAccountOptional.get();

        return mapToUser(userAccount);
    }

    private UserDetails mapToUser(UserAccount userAccount){
        Short deleted = userAccount.getDeleted();
        boolean enable = UserAccount.UserStatus.NORMAL.getStatus() == userAccount.getStatus() && deleted == 0;
        boolean locked = UserAccount.UserStatus.LOCKED.getStatus() == userAccount.getStatus();
        String[] roleCodes = getUserRoleCodes(userAccount);
        UserDetails user = User.withUsername(userAccount.getAccount())
                .accountExpired(false)
                .password(userAccount.getPassword())
                .credentialsExpired(false)
                .disabled(!enable)
                .accountLocked(locked)
                .roles(roleCodes)
                .build();
        Long tenantId = userAccount.getTenantId();
        return new TenantUser(tenantId == null ? null : tenantId.toString(),userAccount.getId().toString(),user);
    }

    private String[] getUserRoleCodes(UserAccount userAccount){
        Employee employee = userAccount.getEmployee();
        List<RoleRelation> userRole = roleRelationRepository.findAllByTargetIdAndTargetTypeAndTenantId(userAccount.getId(), RoleRelation.RelationTargetTypeEnum.USER.getType(), userAccount.getTenantId());
        List<AuthGroupCrew> authGroupCrews = authGroupCrewRepository.findAllByCrewIdAndCrewTypeAndTenantId(userAccount.getId(), RoleRelation.RelationTargetTypeEnum.USER.getType(), userAccount.getTenantId());
        if(employee != null && employee.getStatus() == (short) 1){
            Long positionId = employee.getPositionId();
            Long departId = employee.getDepartId();
            if(positionId != null){
                List<AuthGroupCrew> positionAuthGroup = authGroupCrewRepository.findAllByCrewIdAndCrewTypeAndTenantId(positionId, RoleRelation.RelationTargetTypeEnum.POSITION.getType(), userAccount.getTenantId());
                authGroupCrews.addAll(positionAuthGroup);
                List<RoleRelation> positionRole = roleRelationRepository.findAllByTargetIdAndTargetTypeAndTenantId(positionId, RoleRelation.RelationTargetTypeEnum.POSITION.getType(), userAccount.getTenantId());
                userRole.addAll(positionRole);
            }
            if(departId != null) {
                List<AuthGroupCrew> departAuthGroup = authGroupCrewRepository.findAllByCrewIdAndCrewTypeAndTenantId(departId, RoleRelation.RelationTargetTypeEnum.DEPARTMENT.getType(), userAccount.getTenantId());
                authGroupCrews.addAll(departAuthGroup);
                roleRelationRepository.findAllByTargetIdAndTargetTypeAndTenantId(departId, RoleRelation.RelationTargetTypeEnum.DEPARTMENT.getType(), userAccount.getTenantId());
            }
        }
        Set<Long> groupIdSet = authGroupCrews.stream().map(AuthGroupCrew::getGroupId).collect(Collectors.toSet());
        List<RoleRelation> autGroupRole = roleRelationRepository.findAllByTargetIdInAndTargetTypeAndTenantId(groupIdSet, RoleRelation.RelationTargetTypeEnum.AUTH_GROUP.getType(), userAccount.getTenantId());
        userRole.addAll(autGroupRole);

        return userRole.stream().map(RoleRelation::getRoleCode).distinct().toArray(String[]::new);
    }
}
